package com.example.test.config.aop;

import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author tyd
 * redis分布式锁具体实现类
 */
@Component
public class RedisLocker implements Locker {

    //  脚本逗号之前为设置key-value , 脚本之后为判断 当key不存在的时候,设置key的过期时间
    //  保证 当key不存在的时候设置key的过期时间    此逻辑的原子性
    private final String LUA_LOCK_SCRIPT = "return redis.call('SET', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) ";

    private final RedisScript<String> redisLockScript = new DefaultRedisScript<String>(LUA_LOCK_SCRIPT, String.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public LockContext getKey(String key) {

        String lockKey = RedisConstant.LOCK_KEY + key;

        String lockValue = UUID.randomUUID().toString();

        String result = stringRedisTemplate.execute(redisLockScript,
                stringRedisTemplate.getStringSerializer(),
                stringRedisTemplate.getStringSerializer(),
                Collections.singletonList(lockKey),
                new Object[]{lockValue, "1000"});

        if (StrUtil.isNotEmpty(result) && StrUtil.equalsAnyIgnoreCase(result, "ok")) {
            return new LockContext(lockKey, lockValue);
        }
        return null;
    }

    @Override
    public void releaseLock(LockContext lockContext) {

    }

    @Override
    public void releaseLock(LockContext lockContext, Long expireTime) {
        if (lockContext == null || StrUtil.isEmpty(lockContext.getKey()) || StrUtil.isEmpty(lockContext.getValue())) {
            return;
        }
        String result = stringRedisTemplate.opsForValue().get(lockContext.getKey());
        // 判断是否是同一个锁
        if (!StrUtil.equals(lockContext.getValue(), result)) {
            return;
        }
        if (expireTime > 0) {
            stringRedisTemplate.expire(lockContext.getKey(), expireTime, TimeUnit.MILLISECONDS);
        } else {
            stringRedisTemplate.delete(lockContext.getKey());
        }
    }
}
